import rdbManager from "./rdbManager"
import emb from "./embedding"
import Logger from '../utils/Logger'
import { Caption, simOrig } from "../utils/recallInterface"
import { BusinessError } from "@kit.BasicServicesKit"

export default async function Search1n<T>(name, tableName, candidate = 5, queryDB = true) {
  let vecstrs = await emb([name])
  let vecstr: string = vecstrs[0]
  let vecB = JSON.parse(vecstr)
  let datas
  if (queryDB) {
    datas = await rdbManager.query(tableName)
  } else {
    datas = tableName
  }
  let i = 0
  let data = datas[i]
  let simNames = []
  while (data != undefined) {
    // console.log("_:_[vec1] [" + name + "]" + vecB)
    let simName = getSimName<T>(data, vecB)
    simNames.push(simName)
    data = datas[++i]
  }
  return order<T>(simNames, candidate)
}


export function caps2str(captions: Caption[] | string | undefined, seperater = " "): string {
  if (captions == undefined) {
    return "NOTHING"
  }
  let TAG = "_:_captionsTOstr"
  let captionsj: Caption[]
  try {
    if (typeof captions == "string") {
      captionsj = JSON.parse(captions)
    } else {
      captionsj = captions
    }
    let fullstr = ""
    for (let caption of captionsj) {
      fullstr += caption.words + seperater
    }
    return fullstr
  } catch (e) {
    console.error(TAG, "error", JSON.stringify(e))
    return JSON.stringify(captions)
  }
}

export async function Search1n_1<T>(tableName, candidate = 5, extraText = ""): Promise<simOrig<T>[]> {
  let datas = (await rdbManager.query(tableName)) as any[]
  let vecB
  let contentB
  if (extraText == "") {
    vecB = JSON.parse(datas[datas.length-1].vec)
    contentB = (datas[datas.length-1].name) ? datas[datas.length-1].name : datas[datas.length-1].content
    console.log("_:_[vecBase] [" + caps2str(contentB) + "]" + vecB)
  } else {
    for (let i = 0; i < 3; i++) {
      extraText += +" " + extraText
    }
    vecB = JSON.parse((await emb([extraText]))[0])
    console.log("_:_[vecBase] " + extraText + vecB)
  }
  let simNames = []
  for (let i = 0; i < datas.length - 2; i++) {
    let simName = getSimName<T>(datas[i], vecB)
    simNames.push(simName)
  }
  return order<T>(simNames, candidate)
}

function getSimName<T>(datai, vecB: number[]): simOrig<T> {
  let TAG = "_:_getSimNameOfEach"
  // console.log(TAG,"entered")
  let simName: simOrig<T> = {
    sim: -1,
    origin: undefined,
    type: "TextSim"
  }
  try {
    let veci = JSON.parse(datai.vec)
    console.log("_:_[vec2] [" + caps2str((datai?.content) ? datai.content : datai.name) + "]" + veci)
    // console.log("_:_[vecB] " + vecB + typeof vecB, typeof veci)
    let sim = cosineSimilarity(vecB, veci)
    simName.sim = sim
    simName.origin = datai
    return simName
  } catch (e) {
    console.log(TAG, "error", e, e.message, JSON.stringify(e), JSON.stringify(simName))
    return simName
  }
}

export function order<T>(simNames, candidate): simOrig<T>[] {
  let TAG = "_:_order_simNames"
  simNames.sort((a, b) => {
    return b.sim - a.sim;
  });
  let resultSet: simOrig<T>[] = []
  for (let i = 0; i < Math.min(candidate, simNames.length); i++) {
    console.log("_:_[sim] " + simNames[i].sim + "[" + i + "]=" + caps2str((simNames[i].origin.content)))
    resultSet.push(simNames[i])
  }
  console.log(TAG, "resultset got", JSON.stringify(resultSet))
  return resultSet
}

function dotProduct(A: number[], B: number[]): number {
  return A.reduce((sum, value, index) => sum + value * B[index], 0);
}

function magnitude(A: number[]): number {
  return Math.sqrt(A.reduce((sum, value) => sum + value**2, 0));
}

function cosineSimilarity(A: number[], B: number[]): number {
  const dot = dotProduct(A, B);
  const magA = magnitude(A);
  const magB = magnitude(B);
  if (magA === 0 || magB === 0) {
    console.log('_:_[cosine]!!!! One of the vectors is a zero vector, cannot compute cosine similarity.');
  }
  return dot / (magA * magB);
}
